﻿#pragma once

#include <qobject.h>
#include "ObjAutoInject.h"
#include <qvector.h>
#include <qtimer.h>
#include <qhash.h>
#include <qthread.h>

#include "LogSaver.h"

class TurnTableCodec;
class RegisterCenter_Device;
class ScriptInstruction;
class TransmitCodec;

class ScriptRunner1;
class ScriptRunner : public QObject,public ObjAutoInject
{
  Q_OBJECT
public:
  enum ScriptError{
    ScriptNoError=0,
    ScriptEmpty,
    DeviceOffline,
  };
  friend class InstructionLoop;
public:
  static const uint userId_scriptRunner=0;
private://property
  QHash<uint,ScriptRunner1*> deviceScriptList;

  int interval_instruction = 200;//指令间正常时间间隔
  int interval_connectionTimeOut = 1000;//设置连接超时触发时间，与转台连接状态管理器冲突，
  int interval_turnTableOperationTimeOut = 30000;//转台操作超时上限

private://dependency
  TurnTableCodec* turnTableCodec = Q_NULLPTR;
  RegisterCenter_Device* registerCenter_Device = Q_NULLPTR;
  TransmitCodec* transmitCodec=Q_NULLPTR;
  LogSaver* logSaver = LogSaver::getLogSaver();
public:
  explicit ScriptRunner();
  virtual ~ScriptRunner();

signals:
  //当脚本开始时会触发该信号
  void signal_scriptStarted(uint deviceId);
  //当脚本停止时会触发该信号
  void signal_scriptStoped(uint deviceId);
  //当当前运行指令改变时会触发该信号，参数int codeBlockIndex代表当前所在代码块索引，参数int instructionIndex代表当前指令在当前代码块中的索引
  void signal_currentInstruction(uint deviceId, int instructionIndex);

public:
  //启动脚本，脚本将按照预定计划开始执行
  ScriptError startScript(uint deviceId);
  //正常停止脚本，只停止下一步行动，重置各状态，不作出任何其余的处理，正常退出或者不需要继续脚本操作时使用
  void stopScript(uint deviceId);
public:
  bool getScriptState(uint deviceId);
  void handleTransmitResult(uint deviceId,uint resultType);
  void scriptWaitAndRun(int millisecond,uint deviceId);//等待millisecond毫秒后自动执行下个指令
  void sendInstruction(uint deviceId,uint type,float arg1=0,float arg2=0,float arg3=0);
private slots:
  void slot_turnTableError(uint deviceId);//转台出错时停止脚本
  void slot_timer_wait_timeOut(ScriptRunner1 *scriptRunner1);

public slots:
  void slot_stateCheckerTimeOut(uint deviceId);
  void slot_conditionReached(uint deviceId);
  void slot_targetDeviceMissed(uint deviceId);
  //代码优化，模块化
private:
  //运行下一条指令
  void runNextInstruction(ScriptRunner1* scriptRunner1);
  //重置脚本到初始状态
  void initScript(ScriptRunner1* scriptRunner1);
  //开启一个新的循环，目标代码块和循环次数入栈
  void newLoop(uint deviceId,int loopCount,int instructionCount);
  //循环层销毁，执行完毕的代码块出栈
  void loopFinished(ScriptRunner1* scriptRunner1);
  //检查是否存在下一条可运行的指令
  bool ifCanRun(ScriptRunner1* scriptRunner1);

  //geter and seter
public:
  QList<ScriptInstruction*> getScript(uint deviceId);
  bool setScript(uint deviceId,QList<ScriptInstruction*> script);
  int getInterval_instruction();
  void setInterval_instruction(int interval_instruction);
  int getTime_connectTimeOut();
  void setTime_connectTimeOut(int interval_connectionTimeOut);
  int getInterval_turnTableOperationTimeOut();
  void setInterval_turnTableOperationTimeOut(int interval_turnTableOperationTimeOut);
  TurnTableCodec* getTurnTableCodec();
  void setTurnTableCodec(TurnTableCodec * const turnTableCodec);
  RegisterCenter_Device* getRegisterCenter_Device()const;
  ScriptRunner* setRegisterCenter_Device(RegisterCenter_Device* const registerCenter_Device);
  TransmitCodec* getTransmitCodec()const;
  ScriptRunner* setTransmitCodec(TransmitCodec* const transmitCodec);
public:
  virtual void objectAutoInjection(QHash<QString,QObject*>* objMap)override;
};

class ScriptRunner1 : public QObject
{
  Q_OBJECT
  friend class ScriptRunner;
private:
  QList<ScriptInstruction*> script;
  QList<int> loopStack;//循环栈，用于记录循环的下标
  QList<int> loopCountStack;//循环计数栈，栈顶代表当前循环的剩余循环次数
  QList<int> instructionCountStack;//指令计数栈，栈顶代表当前循环总指令数量，循环中的循环只作为一个指令
  QList<int> remainInstructionCountStack;//剩余指令计数栈，栈顶代表当前循环剩余总指令数量
  bool state_script = false;//用于脚本运行控制
  int currentInstructionIndex = 0;
  uint deviceId=0;
  QTimer timer_wait;
private:
  explicit ScriptRunner1();
  virtual ~ScriptRunner1();
};
